<?php

declare(strict_types=1);

namespace App\Service\Access;

use Hyperf\DbConnection\Db;
use Hyperf\Di\Annotation\Inject;
use Hyperf\HttpServer\Contract\RequestInterface;
use function Hyperf\Support\env;

/**
 * 数据访问基类
 */
class BaseAccess
{
        #[Inject]
        protected RequestInterface $request;

        /**
         * 基础参数
         * @var array|int
         */
        public array|int $wheres = [], $fields = [], $orders = [], $groups = [], $joins = [], $page, $limit;

        /**
         * 构造函数
         */
        public function __construct()
        {
        }

        /**
         * 获取请求参数 方法 键名 默认值
         * @param string $f
         * @param mixed|null $k
         * @param mixed|null $d
         * @return mixed
         */
        public function getRequestPrams(string $f, mixed $k = null, mixed $d = null): mixed
        {
                try {
                        $mixedResult = match ($f) {
                                'all' => $this->request->all(),
                                'inputs' => $this->request->inputs($k, $d),
                                'file' => $this->request->file($k, $d),
                                default => $this->request->input($k, $d),
                        };
                } catch (\Throwable $t) {
                        $mixedResult = $d;
                }
                return $mixedResult;
        }

        /**
         * 输入条件
         * @param object $model
         * @param array $wheres
         * @return object
         */
        public function inputWheres(object $model, array $wheres): object
        {
                foreach ($wheres as $v) {
                        $model = match ($v[0]) {
                                'whereIn' => $model->whereIn($v[1], $v[2]),
                                'whereNotIn' => $model->whereNotIn($v[1], $v[2]),
                                'whereRaw' => $model->whereRaw($v[1]),
                                'whereBetween' => $model->whereBetween($v[1], $v[2]),
                                'whereNotBetween' => $model->whereNotBetween($v[1], $v[2]),
                                default => $model->where($v[0], $v[1], $v[2]),
                        };
                }
                return $model;
        }

        /**
         * 输入字段
         * @param object $model
         * @param array $fields
         * @param string|null $selectRaw
         * @return object
         */
        public function inputFields(object $model, array $fields = [], string|null $selectRaw = null): object
        {
                if ($selectRaw) {
                        $model = $model->selectRaw($selectRaw);
                        if ($fields) $model = $model->addSelect($fields);
                } else {
                        $model = $model->select($fields);
                }
                return $model;
        }

        /**
         * 输入排序
         * @param object $model
         * @param array $orders
         * @return object
         */
        public function inputOrders(object $model, array $orders = []): object
        {
                foreach ($orders as $k => $v) {
                        $model = $model->orderBy($k, $v);
                }
                return $model;
        }

        /**
         * 输入分组
         * @param object $model
         * @param array $groups
         * @return object
         */
        public function inputGroups(object $model, array $groups = []): object
        {
                foreach ($groups as $v) {
                        $model = $model->groupBy($v);
                }
                return $model;
        }

        /**
         * 输入连接
         * @param object $model
         * @param array $joins
         * @return object
         */
        public function inputJoins(object $model, array $joins = []): object
        {
                foreach ($joins as $v) {
                        $strType = strtolower($v['type'] ?? 'inner');
                        $model = match ($strType) {
                                'left' => $model->leftJoin($v['table'], $v['on']),
                                'right' => $model->rightJoin($v['table'], $v['on']),
                                default => $model->join($v['table'], $v['on']),
                        };
                }
                return $model;
        }

        /**
         * 查询分页
         * @param object $model
         * @param int $page
         * @param int $limit
         * @return array
         */
        public function queryPaginate(object $model, int $page = 1, int $limit = 10): array
        {
                return $model->paginate($limit, [], '', $page)->toArray();
        }

        /**
         * 查询指定页
         * @param object $model
         * @param int $page
         * @param int $limit
         * @return array
         */
        public function queryForPage(object $model, int $page = 1, int $limit = 10): array
        {
                return $model->skip((($page - 1) * $limit))->take($limit)->get()->toArray();
        }

        /**
         * 查询一行
         * @param object $model
         * @return array
         */
        public function queryFirst(object $model): array
        {
                return ($objResult = $model->first()) ? $objResult->toArray() : [];
        }

        /**
         * 查询列
         * @param object $model
         * @param string $column
         * @param string|null $key
         * @return array
         */
        public function queryPluck(object $model, string $column, string|null $key = null): array
        {
                return $model->pluck($column, $key)->toArray();
        }

        /**
         * 查询单个值
         * @param object $model
         * @param string $column
         * @return mixed
         */
        public function queryValue(object $model, string $column): mixed
        {
                return $model->value($column);
        }

        /**
         * 查询汇总
         * @param object $model
         * @param string $column
         * @return int
         */
        public function queryCount(object $model, string $column = '*'): int
        {
                return intval($model->count($column));
        }

        /**
         * 主键单个查找
         * @param object $model
         * @param mixed $id
         * @param array $fields
         * @return array
         */
        public function queryFind(object $model, mixed $id, array $fields = ['*']): array
        {
                return ($objResult = $model->find($id, $fields)) ? $objResult->toArray() : [];
        }

        /**
         * 主键多个查找
         * @param object $model
         * @param array $ids
         * @param array $fields
         * @return array
         */
        public function queryFinds(object $model, array $ids, array $fields = ['*']): array
        {
                return $model->find($ids, $fields)->toArray();
        }

        /**
         * 分块批量插入
         * @param object $model
         * @param array $inserts
         * @param int $size
         * @return array
         */
        public function chunkBatchInsert(object $model, array $inserts = [], int $size = 500): array
        {
                if (!$inserts) return [];
                $arrExecResult = [];
                $arrChunkInserts = array_chunk($inserts, $size);
                foreach ($arrChunkInserts as $v) {
                        $arrExecResult[] = $model->insert($v);
                }
                return $arrExecResult;
        }

        /**
         * 分块批量更新
         * @param object $model
         * @param array $updates
         * @param int $size
         * @return array
         */
        public function chunkBatchUpdate(object $model, array $updates = [], int $size = 500): array
        {
                if (!$updates) return [];
                $arrExecResult = [];
                $strTableName = env('DB_PREFIX') . $model->getTable();
                $strPrimaryKey = $model->primaryKey;
                $arrColumns = array_keys($updates[0]);
                unset($arrColumns[array_search($strPrimaryKey, $arrColumns)]); # 移除主键字段
                $arrChunkUpdates = array_chunk($updates, $size);
                foreach ($arrChunkUpdates as $v) {
                        $strSet = '';
                        foreach ($arrColumns as $v2) {
                                $strSet .= " `{$v2}` = CASE ";
                                foreach ($v as $v3) {
                                        $mixedValue = $v3[$v2];
                                        $mixedWhere = $v3[$strPrimaryKey];
                                        $mixedWhen = is_string($mixedWhere) ? "'{$mixedWhere}'" : $mixedWhere;
                                        $mixedThen = is_string($mixedValue) ? "'{$mixedValue}'" : $mixedValue;
                                        $strSet .= " WHEN `{$strPrimaryKey}` = {$mixedWhen} THEN {$mixedThen} ";
                                }
                                $strSet .= ' END,';
                        }
                        $strSet = substr($strSet, 0, -1);
                        $strIds = implode(',', array_column($v, $strPrimaryKey));
                        $strSql = 'UPDATE ' . $strTableName . ' SET ' . $strSet . ' WHERE ' . $strPrimaryKey . ' IN (' . $strIds . ')';
                        $arrExecResult[] = Db::update($strSql);
                }
                return $arrExecResult;
        }

        /**
         * 根据主键id更新
         * @param object $model
         * @param array $update
         * @return int
         */
        public function accordingToPrimaryKeyUpdate(object $model, array $update): int
        {
                $arrWhere = [$model->primaryKey => $update[$model->primaryKey]];
                unset($update[$model->primaryKey]);
                return intval($model->where($arrWhere)->update($update));
        }
}